using System;
using System.IO;
using System.Text;
using System.Drawing;
using System.Resources;
using System.Reflection;
using System.Diagnostics;
using System.Collections;
using System.ComponentModel;
using Microsoft.BizTalk.Message.Interop;
using Microsoft.BizTalk.Component.Interop;
using IComponent = Microsoft.BizTalk.Component.Interop.IComponent;
using Microsoft.BizTalk.Component;
using Microsoft.BizTalk.Messaging;
using System.Text.RegularExpressions;
using System.Xml;
using System.Runtime.InteropServices;
using Microsoft.BizTalk.Component;
    
namespace eliasen.eu.BizTalk.PipelineComponents
{    
    [ComponentCategory(CategoryTypes.CATID_PipelineComponent)]
    [Guid("d03730e0-a8a2-45a3-9519-d4283748911c")]
    [ComponentCategory(CategoryTypes.CATID_Any)]
    public class Promote : BaseCustomTypeDescriptor, IComponent, IBaseComponent, IPersistPropertyBag, IComponentUI, IAssemblerComponent, IDisassemblerComponent
    {
        private IBaseMessage outMsg;

        private static ResourceManager resManager;

        static Promote()
        {
            resManager = Util.GetResourceManager();
        }
        
        public Promote() : base(resManager)
        {
        }

        #region Properties

        private string _constant;

        [BtsDescription("Promote_Property_Constant_Description")]
        [BtsPropertyName("Promote_Property_Constant_Name")]
        public string Constant
        {
            get { return _constant; }
            set { _constant = value; }
        }

        private string _xPath;

        [BtsDescription("Promote_Property_XPath_Description")]
        [BtsPropertyName("Promote_Property_XPath_Name")]
        public string XPath
        {
            get
            {
                return _xPath;
            }
            set
            {
                _xPath = value;
            }
        }
        
        private string _property;

        [BtsDescription("Promote_Property_Property_Description")]
        [BtsPropertyName("Promote_Property_Property_Name")]
        public string Property
        {
            get
            {
                return _property;
            }
            set
            {
                _property = value;
            }
        }
        
        private string _namespace;

        [BtsDescription("Promote_Property_Namespace_Description")]
        [BtsPropertyName("Promote_Property_Namespace_Name")]
        public string Namespace
        {
            get
            {
                return _namespace;
            }
            set
            {
                _namespace = value;
            }
        }

        #endregion

        #region IBaseComponent members
        /// <summary>
        /// Name of the component
        /// </summary>
        [Browsable(false)]
        public string Name
        {
            get
            {
                return resManager.GetString("Promote_Name", System.Globalization.CultureInfo.InvariantCulture);
            }
        }
        
        /// <summary>
        /// Version of the component
        /// </summary>
        [Browsable(false)]
        public string Version
        {
            get
            {
                return resManager.GetString("Promote_Version", System.Globalization.CultureInfo.InvariantCulture);
            }
        }
        
        /// <summary>
        /// Description of the component
        /// </summary>
        [Browsable(false)]
        public string Description
        {
            get
            {
                return resManager.GetString("Promote_Description", System.Globalization.CultureInfo.InvariantCulture);
            }
        }
        #endregion
        
        #region IPersistPropertyBag members
        /// <summary>
        /// Gets class ID of component for usage from unmanaged code.
        /// </summary>
        /// <param name="classid">
        /// Class ID of the component
        /// </param>
        public void GetClassID(out System.Guid classid)
        {
            classid = new System.Guid("d03730e0-a8a2-45a3-9519-d4283748911c");
        }
        
        /// <summary>
        /// not implemented
        /// </summary>
        public void InitNew()
        {
        }
        
        /// <summary>
        /// Loads configuration properties for the component
        /// </summary>
        /// <param name="pb">Configuration property bag</param>
        /// <param name="errlog">Error status</param>
        public virtual void Load(Microsoft.BizTalk.Component.Interop.IPropertyBag pb, int errlog)
        {
            object val = null;
            val = this.ReadPropertyBag(pb, "Namespace");
            if ((val != null))
            {
                this._namespace = ((string)(val));
            }
            val = this.ReadPropertyBag(pb, "Property");
            if ((val != null))
            {
                this._property = ((string)(val));
            }
            val = this.ReadPropertyBag(pb, "XPath");
            if ((val != null))
            {
                this._xPath = ((string)(val));
            }
            val = this.ReadPropertyBag(pb, "Constant");
            if ((val != null))
            {
                this._constant = ((string)(val));
            }
        }
        
        /// <summary>
        /// Saves the current component configuration into the property bag
        /// </summary>
        /// <param name="pb">Configuration property bag</param>
        /// <param name="fClearDirty">not used</param>
        /// <param name="fSaveAllProperties">not used</param>
        public virtual void Save(Microsoft.BizTalk.Component.Interop.IPropertyBag pb, bool fClearDirty, bool fSaveAllProperties)
        {
            this.WritePropertyBag(pb, "Namespace", this.Namespace);
            this.WritePropertyBag(pb, "Property", this.Property);
            this.WritePropertyBag(pb, "XPath", this.XPath);
            this.WritePropertyBag(pb, "Constant", this.Constant);
        }
        
        #region utility functionality
        /// <summary>
        /// Reads property value from property bag
        /// </summary>
        /// <param name="pb">Property bag</param>
        /// <param name="propName">Name of property</param>
        /// <returns>Value of the property</returns>
        private object ReadPropertyBag(Microsoft.BizTalk.Component.Interop.IPropertyBag pb, string propName)
        {
            object val = null;
            try
            {
                pb.Read(propName, out val, 0);
            }
            catch (System.ArgumentException )
            {
                return val;
            }
            catch (System.Exception e)
            {
                throw new System.ApplicationException(e.Message);
            }
            return val;
        }
        
        /// <summary>
        /// Writes property values into a property bag.
        /// </summary>
        /// <param name="pb">Property bag.</param>
        /// <param name="propName">Name of property.</param>
        /// <param name="val">Value of property.</param>
        private void WritePropertyBag(Microsoft.BizTalk.Component.Interop.IPropertyBag pb, string propName, object val)
        {
            try
            {
                pb.Write(propName, ref val);
            }
            catch (System.Exception e)
            {
                throw new System.ApplicationException(e.Message);
            }
        }
        #endregion
        #endregion
        
        #region IComponentUI members
        /// <summary>
        /// Component icon to use in BizTalk Editor
        /// </summary>
        [Browsable(false)]
        public IntPtr Icon
        {
            get
            {
                return new System.IntPtr();
                //return ((System.Drawing.Bitmap)(this.resourceManager.GetObject("COMPONENTICON", System.Globalization.CultureInfo.InvariantCulture))).GetHicon();
            }
        }
        
        /// <summary>
        /// The Validate method is called by the BizTalk Editor during the build 
        /// of a BizTalk project.
        /// </summary>
        /// <param name="obj">An Object containing the configuration properties.</param>
        /// <returns>The IEnumerator enables the caller to enumerate through a collection of strings containing error messages. These error messages appear as compiler error messages. To report successful property validation, the method should return an empty enumerator.</returns>
        public System.Collections.IEnumerator Validate(object obj)
        {
            // example implementation:
            // ArrayList errorList = new ArrayList();
            // errorList.Add("This is a compiler error");
            // return errorList.GetEnumerator();
            return null;
        }
        #endregion
        
        #region IComponent members
        /// <summary>
        /// Implements IComponent.Execute method.
        /// </summary>
        /// <param name="pc">Pipeline context</param>
        /// <param name="inmsg">Input message</param>
        /// <returns>Original input message</returns>
        /// <remarks>
        /// IComponent.Execute method is used to initiate
        /// the processing of the message in this pipeline component.
        /// </remarks>
        public Microsoft.BizTalk.Message.Interop.IBaseMessage Execute(Microsoft.BizTalk.Component.Interop.IPipelineContext pc, Microsoft.BizTalk.Message.Interop.IBaseMessage inmsg)
        {
            DoPromotion(inmsg);
            return outMsg;
        }

        #endregion

        #region Common

        private void DoPromotion(Microsoft.BizTalk.Message.Interop.IBaseMessage inmsg)
        {
            if (string.IsNullOrEmpty(Namespace) || string.IsNullOrEmpty(Property))
                throw new ApplicationException("You cannot have either the namespace or the property values to be empty. The Promote component cannot promote anything if either of these is empty. Please fix it and try again.");

            if (string.IsNullOrEmpty(XPath) && string.IsNullOrEmpty(Constant))
                throw new ApplicationException("You cannot have both the XPath and Constant property to be empty. The Promote component doesn't know what value to promote. Fix this and try again.");

            if (!string.IsNullOrEmpty(Constant))
                inmsg.Context.Promote(Property, Namespace, Constant);
            else
            {
                Stream inStream = inmsg.BodyPart.Data;
                XmlDocument xmldoc = new XmlDocument();
                try
                {
                    xmldoc.Load(inStream);
                    try
                    {
                        XmlNodeList nodelist = xmldoc.SelectNodes(XPath);

                        if (nodelist != null && nodelist.Count == 1)
                        {
                            string value = nodelist[0].InnerText;

                            inmsg.Context.Promote(Property, Namespace, value);
                        }
                        else
                        {
                            if (nodelist != null)
                            {
                                StringBuilder sb = new StringBuilder();
                                sb.Append("The Promote pipeline component encountered an error.\r\n\r\n");
                                sb.Append("The XPath expression ");
                                sb.Append(XPath);
                                sb.Append(" evaluated to more than one possible result.");
                                sb.Append("Make sure that the XPath expression in the \"Promote\" pipeline component in the pipeline is unambiguous and try again.");

                                throw new ApplicationException(sb.ToString());
                            }
                        }
                    }
                    catch (System.Xml.XPath.XPathException xpe)
                    {
                        throw new ApplicationException("The Promote pipeline component failed because the XPath entered in the properties of the component is not a valid XPath expression. Please correct this error and try again.", xpe);
                    }
                }
                catch (System.Xml.XmlException xe)
                {
                    throw new ApplicationException("The Promote pipeline component could not load the message as an XmlDocument. Only well-formed Xml documents are supported.", xe);
                }
            }
            outMsg = inmsg;
        }

        #endregion

        #region IAssemblerComponent Members

        public void AddDocument(IPipelineContext pContext, IBaseMessage pInMsg)
        {
            DoPromotion(pInMsg);
        }

        public IBaseMessage Assemble(IPipelineContext pContext)
        {
            return outMsg;
        }

        #endregion

        #region IDisassemblerComponent Members

        public void Disassemble(IPipelineContext pContext, IBaseMessage pInMsg)
        {
            DoPromotion(pInMsg);
        }

        public IBaseMessage GetNext(IPipelineContext pContext)
        {
            return outMsg;
        }

        #endregion
    }
}
